package PseudoRandomNumberAlgorithm;

import java.math.BigInteger;

// 梅森旋转伪随机算法 MT19937-32
public class MersenneTwist {

    // 该算法含有一些固定的参数
    private static final int w = 32, n = 624, m = 397, r = 31;
    private static final BigInteger a = new BigInteger("9908b0df", 16);
    private static final BigInteger f = new BigInteger("6c078965", 16);

    private static final int u = 11;
    private static final BigInteger d = new BigInteger("ffffffff", 16);

    private static final int s = 7;
    private static final BigInteger b = new BigInteger("9d2c5680", 16);

    private static final int t = 15;
    private static final BigInteger c = new BigInteger("efc60000", 16);

    private static final int l = 18;

    // 非静态变量
    private final BigInteger[] MT = new BigInteger[n];
    private int index = 0;

    public MersenneTwist(String seed) {
        seedInit(seed);
    }

    /**
     * 初始化函数seedInit
     *
     * @param seed 输入一个seed初始化随机数发生器
     */
    private void seedInit(String seed) {
        index = n;
        MT[0] = new BigInteger(seed);
        for (int i = 1; i < n; i++) {
            MT[i] = (f.multiply(MT[i - 1].xor(MT[i - 1].shiftRight(30)))).add(BigInteger.valueOf(i));
            MT[i] = MT[i].and(new BigInteger("ffffffff", 16));
        }
    }

    public void twist() {

        // int lowerMask = (1 << r) - 1, upperMask = ~lowerMask;
        BigInteger lowerMask = BigInteger.ONE.shiftLeft(r).subtract(BigInteger.ONE);
        BigInteger upperMask = BigInteger.ONE.shiftLeft(r);

        for (int i = 0; i < n; i++) {
            BigInteger x = (MT[i].and(upperMask)).add(MT[(i + 1) % n].and(lowerMask));
            BigInteger xA = x.shiftRight(1);
            if (!x.mod(BigInteger.valueOf(2)).equals(BigInteger.ZERO)) {
                xA = xA.xor(a);
            }
            MT[i] = MT[(i + m) % n].xor(xA);
        }
        index = 0;
    }

    public BigInteger extract() {
        if (index == n) {
            twist();
        }
        BigInteger x = MT[index];
        BigInteger y = x.xor((x.shiftRight(u)).and(d));
        y = y.xor((y.shiftLeft(s)).and(b));
        y = y.xor((y.shiftLeft(t)).and(c));
        BigInteger z = y.xor(y.shiftRight(l));

        BigInteger res = z.and(new BigInteger("ffffffff", 16));
        index++;

        return res;//z;
    }

}
